package com.codeyang.controller;

import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.codeyang.constans.Constants;
import com.codeyang.domain.ActiveUser;
import com.codeyang.domain.LoginInfo;
import com.codeyang.domain.User;
import com.codeyang.dto.LoginBodyDto;
import com.codeyang.service.LoginInfoService;
import com.codeyang.service.MenuService;
import com.codeyang.utils.AddressUtils;
import com.codeyang.utils.AjaxResult;
import com.codeyang.utils.IpUtils;
import com.codeyang.utils.ShiroSecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 描述: TO-删除标志-DO
 *
 * @author CodeYang_Site
 * @version 2021/4/20 14:31
 */

@RestController
@RequestMapping("/login")
@Slf4j
public class LoginController {


    @Autowired
    private MenuService menuService;

    @Autowired
    private LoginInfoService loginInfoService;


    /**
     * Validated  启用校验器
     *
     * @param userLoginDto
     * @return
     */
    @PostMapping("/doLogin")
    public AjaxResult doLogin(@RequestBody @Validated LoginBodyDto userLoginDto) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userLoginDto.getUsername(), userLoginDto.getPassword());
        AjaxResult ajaxResult = new AjaxResult();
        LoginInfo loginInfo = getLoginInfo(userLoginDto);
        try {
            subject.login(usernamePasswordToken);
            Serializable token = subject.getSession().getId();
            // 这一步本质是从线程中拿到一个 登录对象 缓存的
            User user = ShiroSecurityUtils.getCurrentUser();
            ajaxResult.put("code", 200);
            ajaxResult.put("msg", "ok");
            ajaxResult.put("token", token);
            setLoginSuccess(loginInfo, user.getUserName());
            return ajaxResult;
        } catch (Exception e) {
            ajaxResult.put("code", 400);
            ajaxResult.put("msg", "用户名或密码错误");
            setLoginFail(loginInfo);
            return ajaxResult;
        } finally {
            // 不管成功还是都写到日志中 --- 可能有异常 --- 写日志操作不重要,成功也行,失败也可以
            try {
                loginInfoService.save(loginInfo);
            } catch (Exception e) {
                log.error("写日志失败:{}", e.getMessage());
            }

        }

    }

    /**
     * 设置登录失败的登录日志
     *
     * @param loginInfo
     */
    private void setLoginFail(LoginInfo loginInfo) {
        loginInfo.setLoginStatus(Constants.STATUS_FALSE);
        loginInfo.setMsg("用户或者密码不正确");
    }

    /**
     * 登录成功日志设置
     *
     * @param loginInfo
     * @param username
     */
    private void setLoginSuccess(LoginInfo loginInfo, String username) {
        loginInfo.setLoginStatus(Constants.STATUS_TRUE);
        // 登录成功后才有用户名
        loginInfo.setUserName(username);
        loginInfo.setMsg("登录成功");
    }

    /**
     * 设置登录的日志信息
     *
     * @param userLoginDto
     * @return
     */
    private LoginInfo getLoginInfo(LoginBodyDto userLoginDto) {
        LoginInfo loginInfo = new LoginInfo();
        // 登录账号 -- 手机号
        loginInfo.setLoginAccount(userLoginDto.getUsername());
        // 登录类型暂时只有管理员 用户类型还没有添加
        loginInfo.setLoginType(Constants.USER_ADMIN);
        loginInfo.setLoginTime(new Date());
        // ip os bower ....
        // mvc 提供获取request对象  -- 本质从线程里面去取值  ---形参里面的Request 对象就是这样获取的
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

        loginInfo.setIpAddr(IpUtils.getIpAddr(requestAttributes.getRequest()));
        loginInfo.setLoginLocation(AddressUtils.getRealAddressByIP(loginInfo.getIpAddr()));
        // os 与 bowser 是在请求头里面的
        HttpServletRequest request = requestAttributes.getRequest();
        //解析请求头
        String header = request.getHeader("User-Agent");
        UserAgent userAgent = UserAgentUtil.parse(header);
        loginInfo.setOs(userAgent.getOs().getName());
        loginInfo.setBrowser(userAgent.getBrowser().getName());
        return loginInfo;
    }

    /**
     * 返回用戶信息
     *
     * @return
     */
    @GetMapping("/getInfo")
    public AjaxResult getUserInfo() {
        User user = ShiroSecurityUtils.getCurrentUser();
        AjaxResult result = AjaxResult.success();
        result.put("username", user.getUserName()); //用户名
        result.put("picture", user.getPicture()); // 头像
        result.put("roles", ShiroSecurityUtils.getCurrentUserRoles()); //角色
        result.put("permissions", ShiroSecurityUtils.getCurrentUserPermissions()); // 权限
        return result;

    }

    /**
     * 菜单树的查询
     *
     * @return
     */
    @GetMapping("/getMenus")
    public AjaxResult getMenus() {
        System.out.println("我被调用了获取菜单");
        /**
         * 1 该方法的调用次数较多,对数据的压力大,我们应该让它只查询一次数据库:: 在登录里面去查询数据库
         * 2 用户的角色查询也会被调用多次,因为我们也只能调用一次,::: 在登录的里面做
         * 存储到redis中去
         */
//        // 通过用户的id查询用户的菜单
//        User user = (User) SecurityUtils.getSubject().getPrincipal();
//        boolean isAdmin = true;
//        List<MenuTreeVo> menuTreeVos = menuService.getMenuIdsByUserId(user.getUserId(), isAdmin);

        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();


        return AjaxResult.success(activeUser.getMenuTreeVos());

    }

    /**
     * 后端退出--shiro-销毁session+redis token
     *
     * @return
     */
    @PostMapping("/logout")
    public AjaxResult loginOut() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        // 最后会去调用 this.subjectDAO.delete(subject); 调用到 redis-shirdo的 del key操作
        return AjaxResult.success("ok");
    }

    public static void main(String[] arge) {
        Date date = new Date(1589989516000L);
        String format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSS'Z'").format(date);
        System.out.println(format);

    }

}
